/***************************************************************************
 *   Copyright (C) 2005 by xihe   *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _FRIEND_LIST_H_
#define _FRIEND_LIST_H_

#include "Packet.h"
#include "QQBuddy.h"
#include <vector>
#include <string>
#include <sstream>

using std::vector;
using namespace std;

inline string int2string(int src)
{
    stringstream s;
    s << src;
    string ret;
    s >> ret;
    return ret;
}

/**
 * 请求得到好友列表的包，其内容为
 * 1. 头部
 * 2. 两个字节的起始好友列表返回位置，注意这个起始位置是排序之后的
 *    加入你有9个好友，号码从10到100每隔10位一个，那么如果这两个数
 *    字的值指定为3，那10, 20, 30就不会返回了
 * 3. 返回的好友列表是否排序，1个字节，非0则排序
 * 4. 2个未知字节
 * 5. 尾部
 */

class GetFriendListPacket : public OutPacket {
    public:
	GetFriendListPacket(QQClient& cl, uint16_t startPosition_ = 0x0000) :
	    OutPacket(cl, QQ_CMD_GET_FRIEND_LIST, SEND_COUNT),
	    startPosition(startPosition_)
	{}
	int putBody(uint8_t* buf) const;

    private:
	uint16_t startPosition;
};


/**
 * 请求好友列表的应答包，格式为
 * 1. 头部
 * 2. 下一次好友列表开始位置，这个位置是你所有好友排序后的位置，如果为0xFFFF，那就是你的好友已经全部得到了
 *    每次都固定的返回50个好友，所以如果不足50个了，那么这个值一定是0xFFFF了
 * 3. 好友QQ号，4字节
 * 4. 未知, 1字节
 * 5. 头像，1字节
 * 6. 年龄，1字节
 * 7. 性别，1字节
 * 8. 昵称长度，1字节
 * 9. 昵称，不定字节，由8指定
 * 10. 2个未知字节
 * 11. 1字节扩展标志，bit1表示是否有QQ Show，其他未知
 * 12. 1字节通用标志
 *     bit1 => 会员
 *     bit4 => TCP方式登陆
 *     bit5 => 移动QQ
 *     bit6 => 绑定到手机
 * 	   bit7 => 是否有摄像头
 * 13. 重复3到11的结构
 * 14.尾部
 */

class GetFriendListReplyPacket : public InPacket {
    protected:
	GetFriendListReplyPacket(const uint8_t* buf, int len);
    public:
	typedef vector<QQBuddy> Members;
	uint16_t getPosition() const { return position; }
	const Members& getMembers() const { return members; }
    private:
	Members	    members;
	uint16_t    position;
};


/**
 * 获取在线好友列表的请求包，格式为
 * 1. 头部
 * 2. 1个字节，只有值为0x02或者0x03时服务器才有反应，不然都是返回0xFF
 *    经过初步的试验，发现3得到的好友都是一些系统服务，号码比如72000001到72000013，
 *    就是那些移动QQ，会员服务之类的；而2是用来得到好友的
 * 3. 1个字节，得到好友列表的位置，也就是position，这个position的含义和
 *    GetFriendListPacket中的position相同，只不过这个position单位是1 byte，
 *    当然也有可能GetFriendListPacket中的position也是1 byte，不好说
 * 4. 1个未知字节，设成0
 * 5. 2个未知字节，设成0
 * 6. 尾部
 */

class GetFriendOnlinePacket : public OutPacket {
    public:
	GetFriendOnlinePacket(QQClient& cl, uint8_t startPosition_ = 0x00):
	    OutPacket(cl, QQ_CMD_GET_FRIEND_ONLINE, SEND_COUNT),
	    startPosition(startPosition_)
	{}

	uint8_t getPosition() const { return startPosition;}

	int putBody(uint8_t* buf) const;

    private:
	uint8_t startPosition;
};



/**
 * 得到在线好友列表的应答包，格式为
 * 1. 头部
 * 2. position，和GetFriendListReplyPacket中的position一样
 * 3. 31字节的FriendStatus结构
 * 4. 2个未知自己
 * 5. 1个字节扩展标志
 * 6. 1个字节通用标志
 * 7. 2个未知字节
 * 8. 结束符，0，应该是c语言的关系，当作字符串结尾
 * 9. 尾部
 * </pre>
 *
 */

class GetFriendOnlineReplyPacket : public InPacket {
    protected:
	GetFriendOnlineReplyPacket(const uint8_t* buf, int len);
    public:
	typedef vector<QQBuddyStatus> Members;

	uint8_t getPosition() const { return position; }

	const Members& getMembers() const { return members; }

    private:
	Members	    members;
	uint8_t	    position;
};

/**
 * 得到好友等级的请求包
 */
class GetFriendLevelPacket : public OutPacket {
    public:
	typedef vector<uint32_t> Members;
	GetFriendLevelPacket(QQClient& cl, const Members _members);  
	int putBody(uint8_t* buf) const ;
    private:
	Members members;
};

class GetFriendLevelReplyPacket : public InPacket {
    protected:
	GetFriendLevelReplyPacket(const uint8_t* buf, int len);
    public:
	typedef vector<QQBuddyLevel> Members;
	const Members& getMembers() const { return members; }
	uint8_t getReplyCode() const { return replyCode; }
    private:
	Members members;
	uint8_t replyCode;
};

/**
  好友状态改变包，这个是从服务器发来的包，格式为
  1. 头部
  2. FriendStatus结构
  3. 用户的QQ号，注意不是好友的，是用户的，四字节
  4. 尾部
  */

class FriendChangeStatusPacket : public InPacket {
    protected:
	FriendChangeStatusPacket(const uint8_t* buf, int len);
    public:
	typedef QQBuddyStatus FriendsType;
	const FriendsType& getFriendStatus() const { return friendStatus; }
    private:
	FriendsType friendStatus;
};


/**
 * 得到用户的信息，格式为
 * 1. 头部
 * 2. 用户QQ号的字符串形式，然后加密
 * 3. 尾部
 *
 */

class GetUserInfoPacket : public OutPacket {
    public:
	GetUserInfoPacket(QQClient& cl, uint32_t _uid):
	    OutPacket(cl, QQ_CMD_GET_USER_INFO, SEND_COUNT),
	    uid(_uid)
	{}
	int putBody(uint8_t* buf) const;
    private:
	uint32_t uid;
};

/**
 * 请求用户信息的回复包，格式为
 * 1. 头部
 * 2. 由ascii 30分隔的各个字段
 * 3. 尾部
 */

class GetUserInfoReplyPacket : public InPacket {
    protected:
	GetUserInfoReplyPacket(const uint8_t* buf, int len);
    public:
	~GetUserInfoReplyPacket() { delete contact; }

	uint32_t getUid() const { return uid; }
	const QQContact* getContact() const { return contact; }

    private:
	QQContact*   contact;
	uint32_t    uid;

};

/**
 * 搜索在线用户的包，格式为
 * 1. 头部
 * 2. 1个字节，表示搜索类型，比如搜索全部在线用户是0x31，自定义搜索是0x30
 * 3. 1字节分隔符: 0x1F
 * 4. 搜索参数
 * 	  i.  对于搜索全部在线用户的请求，是一个页号，用字符串表示，从0开始
 *    ii. 对于自定义搜索类型，是4个域，用0x1F分隔，依次是
 * 		   a. 要搜索的用户的QQ号的字符串形式
 * 		   b. 要搜索的用户的昵称
 * 		   c. 要搜索的用户的email
 *         d. 页号的字符串形式，这后面没有分隔符了，是用0x0结尾的         
 * 5. 尾部
 */

class SearchUserPacket : public OutPacket {
    public:
	SearchUserPacket(QQClient& cl, int _page);
	SearchUserPacket(QQClient& cl, 
		const string& _qqstr,
	       	const string& _nickname,
		const string& _email,
		int _page,
        bool match);
	int putBody(uint8_t* buf) const;
    private:
	string	qqstr;
	string	nickname;
	string	email;
	string	page;
	uint8_t searchType;
    bool matchEntireString;
	static const uint8_t DIVIDER = 0x1F;
	static const uint8_t NULL_FIELD = 0x2D;
	static const uint8_t PERCENT = 0x25;
};

/**
 * 搜索在线用户的回复包，格式为
 * 1. 头部
 * 2. 有两种形式
 *    第一种为搜索到了用户
 * 	  以0x1F相隔的用户数据，其中，一个用户的数据分4个域，域之间用0x1E相隔，四个域为
 * 	  i.   用户QQ号的字符串形式
 *    ii.  用户昵称
 *    iii. 用户所在地区
 *    iv.  用户的头像号码
 *    第二种是没有更多的匹配了，表示本次搜索的全部匹配已取得
 *    i. 字符串"-1"
 * 3. 尾部
 */

class SearchUserReplyPacket : public InPacket {
    protected:
	SearchUserReplyPacket(const uint8_t* buf, int len);
    public:
	typedef vector<QQOnlineUserInfo> Members;
	const Members& getMembers() const { return members; }
    private:
	Members members;
};


/**
 这个是添加好友的时候用的包，这个和AddFriendAuth有什么关系呢，AddFriend
 是最初始的请求包，要加一个好友的时候，首先发这个包，如果对方不需要验证，那
 就是成功了，如果需要验证，就还要继续发请求。这个包格式是
 1. 头部
 2. 要加的人的QQ号的字符串形式
 3. 尾部
 */
class AddFriendPacket : public OutPacket {
    public:
	AddFriendPacket(QQClient& cl, uint32_t _uid):
	    OutPacket(cl, QQ_CMD_ADD_FRIEND, SEND_COUNT),
	    uid(_uid)
	{}
	int putBody(uint8_t* buf) const;
    private:
	uint32_t uid;
};

/**
 这个添加好友的应答包，格式是
 1. 头部
 2. 我的QQ号的字符串形式
 3. 分隔符1字节，0x1F
 4. 回复码，成功还是需要认证
 5. 尾部
 */

class AddFriendReplyPacket : public InPacket {
    protected:
	AddFriendReplyPacket(const uint8_t* buf, int len);
    public:
	uint8_t getReplyCode() const { return replyCode; }
    private:
	uint8_t replyCode;
};
/*
添加好友验证协议
 1. 头部
 2. 对方验证状态，1字节
 3. 要添加好友的QQ号，4字节
 4. 是否允许对方加自己为好友，1字节，0x01允许，0x02不允许
 5. 把好友加到第几组，1字节，我的好友组是0x00，然后以此类推

如果对方验证状态为0x00，表示对方不需要验证
 6. 尾部

如果对方验证状态为0x02，表示对方需要验证
 6. 验证消息字节长度，1字节
 7. 验证消息，6得到的长度
 8. 尾部 
*/
class AddFriendAuthPacket : public OutPacket {
	public:
	AddFriendAuthPacket(QQClient& cl, uint32_t _uid,bool _addback,
						uint8_t _replyCode,uint8_t _mlen,string _message);
	~AddFriendAuthPacket(){}
	int putBody(uint8_t* buf);
	private:
	uint8_t mlen;
	string message;
	uint8_t replyCode;
	uint32_t uid;
	bool addback;
};

/*添加好友验证应答包

注释: (QQ2005)
====================================

应答包
1. 头部
2. 对方验证状态，1字节 0x00表示对方不需要验证，0x02表示对方需要验证
3. 要添加的好友的QQ号，4字节
4. 回复码，1字节，0x00 表示成功
5. 尾部 
*/
class AddFriendAuthReplyPacket : public InPacket {
	protected:
	AddFriendAuthReplyPacket(const uint8_t* buf,int len);
	public:
	uint8_t getOK(){return rplcode;}
	uint32_t getfinaluid(){return uid;}
	private:
	uint8_t rplcode;
	uint32_t uid;
};

#endif // _GET_FRIEND_LIST_H_
